/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http:  // www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "csna51_adaptor.h"
#include <plugin.h>
#include <map>
#include <memory>
#include "recptprinter_hilog.h"

#include "PrinterLibs.h"


using namespace SwanLink::Finance;

#define CHECK_STATUS(cond, status) do {\
    if (!(cond)) {\
        m_iStatus = SUCCESS;\
    } else {\
        m_iStatus = status;\
        return m_iStatus;\
    }\
}while(0)

#define CHECK_STATUS_NO_RET(cond, status) do {\
    if (!(cond)) {\
        m_iStatus = SUCCESS;\
    } else {\
        m_iStatus = status;\
        return;\
    }\
}while(0)

#define PAPER_WIDTH 66  // 纸宽

// 全局默认参数
static PrintFormat g_ipfmt;

// 构造函数和接口函数实现
CSNA51_Adaptor::CSNA51_Adaptor()
{
    m_pTask = new PrinterTask();
    m_iStatus = NOT_INIT;
    m_isOpen = false;
    m_handle = nullptr;
}

CSNA51_Adaptor::~CSNA51_Adaptor()
{
    m_isOpen = false;
}
// 公共接口
int CSNA51_Adaptor::Open(const Transport& tran)
{
    if (tran.mode == AM_SERIAL) {
        m_handle = Port_OpenCOMIO(tran.dev);
    } else if (tran.mode == AM_USB) {
        m_handle = Port_OpenUSBIO(tran.dev);
    } else {
        CHECK_STATUS(true, ERR_TRANS_NOT_SUPPORT);
    }
    CHECK_STATUS(m_handle == nullptr, ERR_TRANS_PARAM_ERROR);
    Port_SetPort(m_handle);
    // 设置基本参数， 正常字体，可切纸
    Pos_SetPrinterBasic(0, 1, 0, 0, 1);
    m_isOpen = true;
    // 获取默认值
    return m_iStatus;
}

int CSNA51_Adaptor::Close()
{
	m_pTask->StopPrint();
    if (m_handle) {
        Port_ClosePort(m_handle);
    }

    m_isOpen = false;
    m_iStatus = ERR_DEVICE_NOT_OPEN;
    m_handle = nullptr;
    return SUCCESS;
}

bool CSNA51_Adaptor::IsOpen()
{
    if (!m_isOpen) {
        return false;
    }
    int status = GetStatus(0, NULL);
    return status == 0;
}

int CSNA51_Adaptor::Reset()
{
	m_pTask->StopPrint();
    bool ret = Pos_Reset();
    CHECK_STATUS(ret == false, ERR_PRINTER_HW_ERROR);
    return m_iStatus;
}

int CSNA51_Adaptor::GetStatus(int module, char *err)
{
    int status = Pos_QueryPrinterErr();

    switch (status) {
    case -1:    // 打印机脱机
        return ERR_PRINTER_CAP_OPEN;
    case -2:    // 打印机上盖打开
        return ERR_PRINTER_CAP_OPEN;
    case -3:    // 打印机缺纸
        return ERR_PRINTER_OUT_OF_PAPER;
    case -4:    // 打印机切刀异常
        return ERR_PRINTER_CUT_ERROR;
    case -5:    // 打印机头片温度过高
        return ERR_PRINTER_OVERHEATING;
    case -6:    // 查询失败
        return ERR_PRINTER_QUERY_ERROR;
    default:
        break;
    }

    return m_iStatus;
}

// 设备功能接口
int CSNA51_Adaptor::GetPrintFormat(PrintFormatType ftt)
{
    m_iStatus = SUCCESS;
    switch (ftt) {
    case PRINT_FORMAT_DENSITY:  //  灰度值
        return g_ipfmt.density;
    case PRINT_FORMAT_BOLD:  //  粗体设置
        return g_ipfmt.bold;
    case PRINT_FORMAT_REVERSE:  //  是否反白
        return g_ipfmt.reverse;
    case PRINT_FORMAT_INVERSION:  //  上下倒置
        return g_ipfmt.inversion;
    case PRINT_FORMAT_LINETHROUGH:  //  删除线
        return g_ipfmt.linethrough;
    case PRINT_FORMAT_SIZE:  //  字符大小
        return g_ipfmt.size;
    case PRINT_FORMAT_ALIGN:  //  对齐方式
        return g_ipfmt.align;
    case PRINT_FORMAT_ITALIC:  //  是否斜体
        return g_ipfmt.italic;
    case PRINT_FORMAT_HRILOC:  //  HRI字符位置条码
        return g_ipfmt.hriloc;
    default:
        m_iStatus = ERR_PRINTER_FORMAT_NOT_SUPPORT;
        break;
    }
    return m_iStatus;
}

void CSNA51_Adaptor::SetPrintFormat(PrintFormatType ftt, int value)
{
    // int ret = 0;
    m_iStatus = SUCCESS;
    switch (ftt) {
    case PRINT_FORMAT_DENSITY:   // 灰度值
        CHECK_STATUS_NO_RET(value < PF_DENSITY_LIGHT || value > PF_DENSITY_DARK, ERR_PRINTER_VALUE_OUT_RANGE);
        g_ipfmt.density = value;
        return;
    case PRINT_FORMAT_BOLD:   // 粗体设置
        g_ipfmt.bold = value;
        return;
    case PRINT_FORMAT_REVERSE:  // 是否反白
        g_ipfmt.reverse = value;  // 打印时设置
        return;
    case PRINT_FORMAT_INVERSION:  //  上下倒置
        g_ipfmt.inversion = value;  // 打印时设置
        break;
    case PRINT_FORMAT_LINETHROUGH:  // 删除线
        CHECK_STATUS_NO_RET(value < PF_LINETHROUGH_NONE || value > PF_LINETHROUGH_BROKEN,
            ERR_PRINTER_VALUE_OUT_RANGE);
        g_ipfmt.linethrough = value;
        break;
    case PRINT_FORMAT_SIZE:  // 字符大小
        CHECK_STATUS_NO_RET(value < PF_SIZE_EXTRASMALL || value > PF_SIZE_EXTRALARGE,
            ERR_PRINTER_VALUE_OUT_RANGE);
        g_ipfmt.size = value;
        break;
    case PRINT_FORMAT_ALIGN:  // 对齐方式
        CHECK_STATUS_NO_RET(value < PF_ALIGN_LEFT || value > PF_ALIGN_RIGHT,
            ERR_PRINTER_VALUE_OUT_RANGE);
        g_ipfmt.align = value;
        break;
    case PRINT_FORMAT_ITALIC:  // 是否斜体
        g_ipfmt.italic = value;
        break;
    case PRINT_FORMAT_HRILOC:  // HRI字符位置, 条码
        CHECK_STATUS_NO_RET(value < PF_HRILOC_NONE || value > PF_HRILOC_UPDOWN,
            ERR_PRINTER_VALUE_OUT_RANGE);
        g_ipfmt.hriloc = value;
        break;
    default:
        m_iStatus = ERR_PRINTER_FORMAT_NOT_SUPPORT;
        break;
    }
}

void CSNA51_Adaptor::GetDeviceSpec(DeviceSpec &spec)
{
    spec.setDensity = false;  // 不支持
    spec.width = PAPER_WIDTH;
    spec.canCutPaper = false;
    m_iStatus = SUCCESS;
}

int CSNA51_Adaptor::FeedPaper(int line)
{
    CHECK_STATUS(m_isOpen != true, ERR_DEVICE_NOT_OPEN);
    CHECK_STATUS(line < 0 || line > 255, ERR_PRINTER_VALUE_OUT_RANGE);
    bool ret = Pos_Feed_N_Line(line);
    CHECK_STATUS(!ret, ERR_PRINTER_HW_ERROR);
    return m_iStatus;
}

int CSNA51_Adaptor::CutPaper(int line,  int mode)
{
    RECPTPRINTER_HILOGI(RECPTPRINTER_PLUGIN, "CutPaper line = %{public}d, mode = %{public}d", line, mode);
    CHECK_STATUS(line < 0 || line > 255, ERR_PRINTER_VALUE_OUT_RANGE);
    CHECK_STATUS((mode != PF_CUTMODE_ALL) && (mode != PF_CUTMODE_HALF), ERR_PRINTER_VALUE_OUT_RANGE);
    bool ret = false;
    switch (mode) {
    case PF_CUTMODE_ALL:
        ret = Pos_FullCutPaper();
        break;
    case PF_CUTMODE_HALF:
        ret = POS_HalfCutPaper();
        break;
    default:
        break;
    }
    CHECK_STATUS(!ret, ERR_PRINTER_HW_ERROR);
    return m_iStatus;
}

int CSNA51_Adaptor::AddText(int x, int y, const std::string& text)
{
    RECPTPRINTER_HILOGI(RECPTPRINTER_PLUGIN, "AddText x = %{public}d, y = %{public}d, text = %{public}s", x, y, text.c_str());
    CHECK_STATUS(x < 0 || x > PAPER_WIDTH, ERR_PRINTER_VALUE_OUT_RANGE);
    CHECK_STATUS(y < 0 || y > 31, ERR_PRINTER_VALUE_OUT_RANGE);
    
	return m_pTask->AddTask(PT_TYPE_TEXT, x, y, g_ipfmt, text);
}

int CSNA51_Adaptor::AddBitmap(int x, int y, const std::string& lpPath)
{
    RECPTPRINTER_HILOGI(RECPTPRINTER_PLUGIN, "AddBitmap x = %{public}d, y = %{public}d, text = %{public}s", x, y, lpPath.c_str());
    CHECK_STATUS(x < 0 || x > PAPER_WIDTH, ERR_PRINTER_VALUE_OUT_RANGE);
    CHECK_STATUS(y < 0 || y > 31, ERR_PRINTER_VALUE_OUT_RANGE);
	
	CHECK_STATUS(lpPath.empty(), ERR_PRINTER_DATA_TOO_SHORT);
    return m_pTask->AddTask(PT_TYPE_BITMAP, x, y, g_ipfmt, lpPath);
}

int CSNA51_Adaptor::AddQRCode(int x, int y, int w, const std::string& qr)
{
    RECPTPRINTER_HILOGI(RECPTPRINTER_PLUGIN, "AddQRCode x = %{public}d, y = %{public}d, text = %{public}s", x, y, qr.c_str());
    CHECK_STATUS(x < 0 || x > PAPER_WIDTH, ERR_PRINTER_VALUE_OUT_RANGE);
    CHECK_STATUS(y < 0 || y > 31, ERR_PRINTER_VALUE_OUT_RANGE);
    CHECK_STATUS(w < 1 || w > 6, ERR_PRINTER_VALUE_OUT_RANGE);
	CHECK_STATUS(qr.empty(), ERR_PRINTER_DATA_TOO_SHORT);
	CHECK_STATUS(qr.length() > 112, ERR_PRINTER_DATA_TOO_LONG);
	
	PrintFormat fmt = g_ipfmt;
    fmt.width = w;
	return m_pTask->AddTask(PT_TYPE_QRCODE, x, y, fmt, qr);
}

int CSNA51_Adaptor::AddBarCode(int x, int y, BarCodeSpec &spec, const std::string& bar)
{
    RECPTPRINTER_HILOGI(RECPTPRINTER_PLUGIN, "AddBarCode x = %{public}d, y = %{public}d, text = %{public}s", x, y, bar.c_str());
    RECPTPRINTER_HILOGI(RECPTPRINTER_PLUGIN, "AddBarCode align = %{public}d, wide = %{public}d, height = %{public}d, hri = %{public}d",
        g_ipfmt.align, spec.wide, spec.height, spec.hri);
    CHECK_STATUS(x < 0 || x > PAPER_WIDTH, ERR_PRINTER_VALUE_OUT_RANGE);
    CHECK_STATUS(y < 0 || y > 31, ERR_PRINTER_VALUE_OUT_RANGE);
    CHECK_STATUS(spec.height < 1 || spec.height > 255, ERR_PRINTER_VALUE_OUT_RANGE);
    CHECK_STATUS(spec.narrow < 1 || spec.narrow > 3, ERR_PRINTER_VALUE_OUT_RANGE);
    CHECK_STATUS(spec.wide < 1 || spec.wide > 6, ERR_PRINTER_VALUE_OUT_RANGE);
	CHECK_STATUS((spec.hri < PF_HRILOC_NONE) || (spec.hri > PF_HRILOC_UPDOWN), ERR_PRINTER_VALUE_OUT_RANGE);
    CHECK_STATUS(bar.empty(), ERR_PRINTER_DATA_TOO_SHORT);
    CHECK_STATUS(bar.length() > 26, ERR_PRINTER_DATA_TOO_LONG);
    CHECK_STATUS(bar.find_first_not_of("0123456789") != std::string::npos, ERR__PRINTER_DATA_PACKET_ERROR);

    PrintFormat fmt = g_ipfmt;
    int w = spec.narrow * spec.wide;
    fmt.width =  w > 6 ? 2: 1;  // 实际范围[1, 2]
    fmt.height = spec.height;
    fmt.hriloc = spec.hri;
    RECPTPRINTER_HILOGI(RECPTPRINTER_PLUGIN, "AddBarCode task  w1 = %{public}d, w2 = %{public}d, text = %{public}s", w, fmt.width, bar.c_str());

    return m_pTask->AddTask(PT_TYPE_BARCODE, x, y, fmt, bar);
}

void CSNA51_Adaptor::StartPrint(int timeout, PrintListener *listener)
{
    m_iStatus = m_pTask->StartPrint(timeout, listener);
}

// 插件接口实现
std::shared_ptr<IAdaptor> CreateAdaptor()
{
    return std::make_shared<CSNA51_Adaptor>();
}
Transport GetAccess(ACCESS_MODE mode, const char *dev)
{
    Transport tran;
    tran.mode = mode;
    strncpy(tran.dev, dev, sizeof(tran.dev));
    tran.timeout = 3000;
    return tran;
}

static Plugin g_plugin = {
    .CreateAdaptor = CreateAdaptor,
    .GetAccess = GetAccess,
    .version = "1.0",
    .name = "CSNA51",
    .desc = "CSNA51 Printer Adaptor 1.0",
    .id = 0x0202   //
};

PLUGIN_INIT(&g_plugin)
